<template>
  <div class="base-layout-tabs">
    <xt-tabs
      v-model="activeTab"
      :options="tabs"
      :alias="{ name: 'title', code: 'path' }"
      @tab-click="clickTab"
    >
      <template #label="{ name, code, icon, i18n }">
        <div
          class="item-wrap w-full h-full flex flex-row items-center px-16 rounded-t-8 select-none relative -top-[1px]"
          @contextmenu="e => handleContextmenu(e, code)"
        >
          <span class="mr-10">
            <i class="iconfont" :class="icon"></i>
          </span>
          <span>{{ setI18n(i18n, name) }}</span>
          <span
            v-if="code !== '/dashboard'"
            class="close-icon inline-block w-16 h-16 overflow-hidden ml-10 first-letter:hover:opacity-50 relative cursor-pointer"
            @click.stop="closeTabByIcon(code)"
          >
            <i
              class="iconfont icon-guanbi_o absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-12"
            />
          </span>
          <span class="line inline-block w-[1px] h-16 bg-[#1f2225] absolute -right-[1px]" />
        </div>
      </template>
    </xt-tabs>
  </div>

  <!-- 鼠标右键弹出层 -->
  <span
    v-if="pointInfo.show"
    class="point-item fixed z-10 inline-block pointer-events-none"
    :style="{
      left: `${pointInfo.x}px`,
      top: `${pointInfo.y}px`,
      width: `${pointInfo.w}px`,
      height: `${pointInfo.h}px`,
    }"
  >
    <xt-dropdown
      :options="dropdownOptions"
      trigger="click"
      @visible-change="visibleChange"
      @command="handleCommand"
    >
      <span
        ref="dropdownItem"
        class="inline-block absolute left-0 top-0"
        :style="{
          width: `${pointInfo.w}px`,
          height: `${pointInfo.h}px`,
        }"
      />
    </xt-dropdown>
  </span>
</template>

<script lang="ts">
export default {
  name: "BaseLaoutTabs",
};
</script>

<script lang="ts" setup>
import { ref, getCurrentInstance, watch, computed, nextTick } from "vue";
import { RouteLocationNormalized } from "vue-router";
import { PlainObjType } from "@/typings";
import { TabsPaneContext } from "element-plus";
import dashboardRoutes from "@/router/routes/dashboard";
import { useLocalI18n } from "@/locales/i18n";
const { setI18n } = useLocalI18n();

interface TabsType {
  path: string;
  title: string;
  i18n: string;
  icon: string;
  query: PlainObjType;
}

interface CommandEventType {
  refresh: Function;
  closeTabByMouse: Function;
  closeAllTabs: Function;
}

// vue 实例
const curIns = getCurrentInstance()?.proxy;

// 当前激活的 tab
const activeTab = ref<string>(curIns?.$route.path || "");

// 默认 tabs
const dashboard = dashboardRoutes[0];
const defaultTabs: TabsType[] = [
  {
    path: dashboard.path,
    title: dashboard.meta?.title as string,
    i18n: dashboard.meta?.i18n as string,
    icon: dashboard.meta?.icon as string,
    query: {},
  },
];

// 获取浏览器缓存数据
const cacheTabs = JSON.parse(window.sessionStorage.getItem("Tabs") || JSON.stringify(defaultTabs));
const tabs = ref<TabsType[]>(cacheTabs);
// 设置 tabs
const setTabs = (to: RouteLocationNormalized) => {
  // 当前路由已被缓存到 tabs 时，不执行任何操作；否则添加到 tabs
  const isExist = tabs.value.some(i => i.path === to.path);
  if (isExist) return false;

  const tab: TabsType = {
    path: to.path,
    title: to.meta.title as string,
    i18n: to.meta.i18n as string,
    icon: to.meta.icon as string,
    query: to.query,
  };

  // 添加 tab
  tabs.value.push(tab);

  // 存储到浏览器缓存中
  window.sessionStorage.setItem("Tabs", JSON.stringify(tabs.value));
};

// 点击标签跳转对应路由
const clickTab = (pane: TabsPaneContext, ev: Event) => {
  if (curIns?.$route.path === pane.paneName) return false;

  const tab = tabs.value.find(i => i.path === pane.paneName) as TabsType;
  curIns?.$router.push({
    path: tab.path,
    query: tab.query,
  });
};

// 关闭标签(tab 标签关闭 icon)
const closeTabByIcon = (path: string) => {
  const curIndex = tabs.value.findIndex(i => i.path === path);
  const isLast = curIndex === tabs.value.length - 1;

  let nextRoute = null;

  if (activeTab.value === path) {
    // 关闭的是当前激活的 tab 时
    // 当前 tab 是最后一个时，路由跳转到上一个，否则跳转至下一个
    nextRoute = isLast ? tabs.value[curIndex - 1] : tabs.value[curIndex + 1];
  } else {
    // 关闭的不是当前激活的 tab 时，只需关闭对应的 tab 即可，不用跳转路由
  }

  // 存储到浏览器缓存中
  tabs.value = tabs.value.filter(i => i.path !== path);
  window.sessionStorage.setItem("Tabs", JSON.stringify(tabs.value));

  // nextRoute 有值时，跳转对应路由
  if (nextRoute) {
    curIns?.$router.push({
      path: nextRoute.path,
      query: nextRoute.query,
    });
  }
};

// 计算下拉菜单数据
const dropdownOptions = computed(() => {
  let options = [
    {
      name: "刷 新",
      code: "refresh",
      icon: "icon-shuaxin",
      i18n: "tab.REFRESH",
    },
    {
      name: "关闭当前",
      code: "closeTabByMouse",
      icon: "icon-guanbi_o",
      i18n: "tab.CLOSE",
    },
    {
      name: "关闭所有",
      code: "closeAllTabs",
      icon: "icon-guanbi",
      i18n: "tab.CLOSEALL",
      divided: true,
    },
  ];

  // 鼠标右键在首页时，不展示关闭当前
  if (pointInfo.value.path === "/dashboard") {
    options = options.filter(i => i.code !== "closeTabByMouse");
  }

  return options;
});

// 鼠标右键位置信息
const pointInfo = ref({
  show: false,
  path: "",
  w: 1,
  h: 1,
  x: 0,
  y: 0,
});
// 下拉菜单点里面的 span 元素，用于模拟点击弹出浮层
const dropdownItem = ref();
// 重新定义鼠标右键功能
const handleContextmenu = async (e: MouseEvent, path: string) => {
  // 阻止浏览器的默认行为
  e.preventDefault();

  // 隐藏下拉菜单点
  pointInfo.value.show = false;
  await nextTick();

  // 设鼠标右击位置的信息到下拉菜单的点中
  pointInfo.value = Object.assign(pointInfo.value, {
    x: e.pageX - pointInfo.value.w / 2,
    y: e.pageY - pointInfo.value.h / 2,
    show: true,
    path,
  });

  await nextTick();

  console.log(pointInfo.value);

  // 模拟 dropdownItem 点击，弹出下拉菜单浮层
  dropdownItem.value.click();
};

// 下拉菜单 visible 变化时
const visibleChange = (val: boolean) => {
  if (!val) {
    pointInfo.value.show = false;
  }
};

// 刷表右键刷新指定的 tab 页面
const refresh = () => {
  // 当前需要刷新的是哪一个路由
  const route = tabs.value.find(i => i.path === pointInfo.value.path);

  if (!route) return false;
  curIns?.$router.replace({
    path: route.path,
    query: {
      ...route.query,
      t: Date.now(),
    },
  });
};

// 关闭鼠标右键指定的 tab
const closeTabByMouse = () => {
  closeTabByIcon(pointInfo.value.path);
};

// 鼠标右键关闭所有的 tabs
const closeAllTabs = () => {
  // 存储到浏览器缓存中
  tabs.value = tabs.value.filter(i => i.path === "/dashboard");
  window.sessionStorage.setItem("Tabs", JSON.stringify(tabs.value));

  const route = tabs.value[0];
  if (route) {
    curIns?.$router.push({
      path: route.path,
      query: route.query,
    });
  }
};

// 下拉菜单 command 事件
const commandEvent: CommandEventType = {
  refresh,
  closeTabByMouse,
  closeAllTabs,
};

// 下拉菜单 command
const handleCommand = (command: keyof CommandEventType) => {
  commandEvent[command]();
};

// 监听路由变更
watch(
  () => curIns?.$route,
  (to: RouteLocationNormalized | undefined) => {
    if (to) {
      activeTab.value = to.path;
      setTabs(to);
    }
  },
  {
    immediate: true,
    deep: true,
  }
);
</script>

<style lang="scss" scoped>
$HEIGHT: 32px; // tabs 高度
$CIRCLE: 12px; // 圆角 before、after 大小
$CIRCLE_ACTIVE_BG: #f0f0ff; // 圆角 is-active 时的背景色
$CIRCLE_INACTIVE_BG: #dee1e6; // 圆角 hover 时的背景色
$TAB_ACTIVE_COLOR: #646cff; // tab is-active 时颜色
$TAB_INACTIVE_COLOR: #333639; // tab hover 时颜色
$SPACING: 0px; // tab 间的宽度

.base-layout-tabs {
  padding-top: 9px;
  background-color: #fff;
  border-top: 1px var(--el-menu-border-color) solid;

  :deep(.el-tabs) {
    .el-tabs__header {
      margin-bottom: 0;

      .el-tabs__nav-wrap {
        &::after {
          display: none;
        }

        .el-tabs__active-bar {
          display: none;
        }

        .el-tabs__nav-prev,
        .el-tabs__nav-next {
          height: $HEIGHT;
          line-height: 36px;
        }

        .el-tabs__nav {
          padding: 0 10px;
          box-sizing: border-box;

          .el-tabs__item {
            position: relative;
            z-index: 0;
            height: $HEIGHT;
            padding: 0 $SPACING;
            color: $TAB_INACTIVE_COLOR;

            // 常规默认 tab 样式，非 hover is-active
            .item-wrap {
              &::before {
                position: absolute;
                bottom: 0;
                left: -$CIRCLE;
                display: inline-block;
                width: $CIRCLE;
                height: $CIRCLE;
                content: "";
              }

              &::after {
                position: absolute;
                right: -$CIRCLE;
                bottom: 0;
                display: inline-block;
                width: $CIRCLE;
                height: $CIRCLE;
                content: "";
              }
            }

            // is-active hover 共用的样式处理
            &.is-active,
            &:hover {
              .line {
                display: none;
              }
            }

            // tabs is-active 时（蓝色）
            &.is-active {
              z-index: 2;
              color: $TAB_ACTIVE_COLOR;

              .item-wrap {
                background-color: $CIRCLE_ACTIVE_BG;

                &::before {
                  background: radial-gradient(
                    circle at 0% 0%,
                    transparent $CIRCLE,
                    $CIRCLE_ACTIVE_BG $CIRCLE
                  );
                }

                &::after {
                  background: radial-gradient(
                    circle at 100% 0%,
                    transparent $CIRCLE,
                    $CIRCLE_ACTIVE_BG $CIRCLE
                  );
                }
              }
            }

            // tabs hover 时（灰色）
            &:hover:not(.is-active) {
              z-index: 1;
              color: $TAB_INACTIVE_COLOR;

              .item-wrap {
                background-color: $CIRCLE_INACTIVE_BG;

                &::before {
                  background: radial-gradient(
                    circle at 0% 0%,
                    transparent $CIRCLE,
                    $CIRCLE_INACTIVE_BG $CIRCLE
                  );
                }

                &::after {
                  background: radial-gradient(
                    circle at 100% 0%,
                    transparent $CIRCLE,
                    $CIRCLE_INACTIVE_BG $CIRCLE
                  );
                }
              }
            }
          }
        }
      }
    }
  }
}
</style>
